﻿//namespace Proto
//{
//    using System;
//    using System.Collections.Generic;
//    using System.Threading;
//    using Proto.Fields;
//    using Proto.Providers;
//    using Proto.Util;

//    public interface IRequiresService
//    {
//        void RecieveProcessing();
//        event Action<IRequiresService> RequestService;
//    }

//    public delegate void ChangePending<in T>(T addition );
//    public delegate void Changed<in T>(T removal );

//    public class Sub<T> : ISub<T> where T : class, IFieldGetter
//    {
//        internal static int REF_COUNT = 0;
//        ~Sub()
//        {
//            REF_COUNT--;
//        }

//        private readonly IKey m_SubscriptionKey;
//        private Dictionary<IKey, SubItem<T>> m_ItemMap;
//        private Queue<UpdateItem> m_PendingUpdates;
//        private DateTime m_LastUpdate;

//        public Sub( IKey subscription_key ) : this( subscription_key, null){}

//        public Sub( IKey subscription_key, Runner auto_register )
//        {
//            REF_COUNT++;
//            m_SubscriptionKey = subscription_key;
//            m_ItemMap = new Dictionary<IKey, SubItem<T>>( 1000 );
//            m_PendingUpdates = new Queue<UpdateItem>();
//            if( auto_register != null ) auto_register.Register( this );
//        }

//        public void Die()
//        {
//            // because of threading concerns we route this back onto app thread.
//            Action action = new Action(
//                delegate
//                    {
//                        RequestService = delegate { };

//                        foreach (SubItem<T> sub in m_ItemMap.Values)
//                        {
//                            sub.Die();
//                        }

//                        m_ItemMap = null;
//                        m_PendingUpdates = null;
//                    });

//            RunnerRegistry.APP.Enqueue(action);
//        }

//        public IKey SubscriptionKey
//        {
//            get { return this.m_SubscriptionKey; }
//        }

//        public SubState CurrentState
//        {
//            get
//            {
//                if( m_LastUpdate == default(DateTime) ) return SubState.Uninitialized;
//                if( m_PendingUpdates.Count == 1 ) return SubState.UpdatePending;
//                if (m_PendingUpdates.Count > 1 ) return SubState.InBatch;
//                if( m_ItemMap.Count == 0 ) return SubState.Uninitialized;
//                return SubState.Ready;
//            }
//        }

//        public void NotifyAdditionLoader( ChangePending<SubItem<T>> addition_delegate )
//        {
//            Action action = new Action(
//                delegate
//                    {
//#if DEBUG
//                        if( Thread.CurrentThread != RunnerRegistry.APP.Thread )
//                        {
//                            throw new ApplicationException();
//                        }
//#endif
//                        foreach (SubItem<T> item in m_ItemMap.Values)
//                        {
//                            addition_delegate(item);
//                        }
//                        NotifyAdditionPending += addition_delegate;
//                    }
//                    );

//            RunnerRegistry.APP.Enqueue( action );
//        }

//        public void RecieveProcessing()
//        {
//            // TODO: Refactor
//            if (m_PendingUpdates == null || m_PendingUpdates.Count == 0) return;

//            Queue<UpdateItem> next_batch = new Queue<UpdateItem>( 1000 );
//            Queue<UpdateItem> working_set = Interlocked.Exchange( ref m_PendingUpdates, next_batch );

//            // because Die() may be called between null check and interlocked.
//            if (working_set == null) return;


//            Dictionary<IKey, T> new_state = new Dictionary<IKey, T>( 1000 );
//            Dictionary<IKey, T> remove_set = null;
//            bool has_clear = false;

//            // because even though we've interlocked the exchange, a modification may be in progress
//            // this has really thrown in testing.
//            Thread.Sleep(1);

//            while( working_set.Count > 0 )
//            {
//                UpdateItem update = working_set.Dequeue();
//                if (update.UpdateType == UpdateType.Add || update.UpdateType == UpdateType.Update)
//                {
//                    new_state[update.Item.Key] = update.Item;
//                }
//                else if (update.UpdateType == UpdateType.Remove)
//                {
//                    if (remove_set == null) remove_set = new Dictionary<IKey, T>(100);
//                    remove_set[update.Item.Key] = update.Item;
//                }
//                else if (update.UpdateType == UpdateType.Clear)
//                {
//                    has_clear = true;
//                    new_state.Clear();
//                    if (remove_set != null) remove_set.Clear();
//                }
//            }
//            // end state should be nulls in owning set where removals should happen.
//            // values where add or change should happen.

//            if( has_clear )
//            {
//                NotifyClearPending();
//                foreach( T item in new_state.Values )
//                {
//                    this.HandleAddOrUpdate( item );
//                }
//                return;
//            }
//            if (remove_set != null)
//            {
//                foreach (T item in remove_set.Values)
//                {
//                    if (m_ItemMap.ContainsKey(item.Key))
//                    {
//                        HandleRemove(item);
//                    }
//                }
//            }
//            foreach( T item in new_state.Values )
//            {
//                this.HandleAddOrUpdate( item );
//            }
//        }

//        private void HandleAddOrUpdate( T item )
//        {
//            // TODO: check proper thread / batch process

//            SubItem<T> sub;
//            Dictionary<IKey, SubItem<T>> local_map = m_ItemMap;
//            if (local_map == null) return;

//            if (local_map.TryGetValue(item.Key, out sub) == false)
//            {
//                sub = new SubItem<T>( item.Key );
//                if (NotifyAdditionPending != null)
//                    NotifyAdditionPending(sub);

//                local_map.Add(item.Key, sub);
//            }

//            sub.EnqueueUpdate(item);
//            sub.ProcessOutstandingUpdates();
//        }

//        private void HandleRemove( T item )
//        {
//            SubItem<T> sub;
//            if( m_ItemMap.TryGetValue( item.Key, out sub ) == false )
//            {
//#if DEBUG
//                throw new ArgumentException( "Unable remove item: " + item.Key );
//#endif
//            }
//            else
//            {
//                NotifyRemovalPending(sub);
//                m_ItemMap.Remove(item.Key);
//            }
//        }

//        public void EnqueueRemoval(T update)
//        {
//            m_LastUpdate = QuickTime.Now;
//            m_PendingUpdates.Enqueue(new UpdateItem(update, UpdateType.Remove));
            
//            RequestService(this);
//        }

//        public void EnqueueAddition(T update)
//        {
//            m_LastUpdate = QuickTime.Now;
//            m_PendingUpdates.Enqueue(new UpdateItem(update, UpdateType.Add));
            
//            RequestService(this);
//        }

//        public void EnqueueBatchAddition(IEnumerable<T> update_list)
//        {
//            m_LastUpdate = QuickTime.Now;
//            foreach (T item in update_list)
//            {
//                m_PendingUpdates.Enqueue( new UpdateItem( item, UpdateType.Add ) );
//            }

//            RequestService(this);
//        }

//        public void EnqueueUpdate(T update)
//        {
//            m_LastUpdate = QuickTime.Now;
//            m_PendingUpdates.Enqueue( new UpdateItem(update, UpdateType.Update ));
            
//            RequestService(this);
//        }

//        public void EnqueueBatchRemoval(IEnumerable<T> update_list)
//        {
//            m_LastUpdate = QuickTime.Now;
            
//            foreach (T item in update_list)
//            {
//                m_PendingUpdates.Enqueue( new UpdateItem( item, UpdateType.Remove ) );
//            }

//            RequestService(this);
//        }

//        public void EnqueueClear()
//        {
//            m_LastUpdate = QuickTime.Now;
//            m_PendingUpdates.Enqueue(new UpdateItem(null, UpdateType.Clear));
            
//            RequestService(this);
//        }

//        public event ChangePending<SubItem<T>> NotifyAdditionPending;
//        public event ChangePending<SubItem<T>> NotifyRemovalPending = delegate { };
//        public event Action NotifyClearPending = delegate { };
//        public event Action<IRequiresService> RequestService = delegate { };
        
//        private enum UpdateType : short
//        {
//            Add, Remove, Update, Clear
//        }

//        private struct UpdateItem
//        {
//            public readonly UpdateType UpdateType;
//            public readonly T Item;

//            public UpdateItem(T item, UpdateType type)
//            {
//                if( item == null && type != UpdateType.Clear )throw new ArgumentException( "Item cannot be null unless type is Clear");
//                Item = item;
//                UpdateType = type;
//            }
//        }
//    }
//}